/** @file    WeatherInfo.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WeatherInfo.cpp 2809 2010-07-30 05:20:06Z alex $
 *  @brief
 * File containing methods for the wosh::xtended::WeatherInfo class.
 * The header for this class can be found in WeatherInfo.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "WeatherInfo.h"

 #include <core/ObjectAllocator.h>
 #include <core/Utilities.h>
 #include <core/DataModels.h>


using namespace std;
namespace wosh {
 namespace xtended {

 WOSH_REGISTER(wosh::xtended::WeatherInfo, "wosh::ISerializable", _WeatherInfo_VERSION, _static_WeatherInfo )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WeatherInfo::clear() {
	this->source_id = 0;
	this->timestamp = 0;
	this->ttl = 0;
	this->reliability = 0;

	this->period_name = "";
	this->period_start = 0;
	this->period_end = 0;

	this->condition = static_cast<int>(WeatherInfo::CONDITION_UNKNOWN);
	this->condition_x = static_cast<int>(WeatherInfo::CONDITION_XTRA_NONE);
	this->condition_key = "";
	this->condition_summary = "";

	this->temperature = _WeatherInfo_TemperatureUndefined;
	this->temperature_highest = _WeatherInfo_TemperatureUndefined;
	this->temperature_lowest = _WeatherInfo_TemperatureUndefined;

	this->wind_chill = 0;
	this->wind_direction = 0;
	this->wind_speed = 0.0;

	this->sunrise = -1;
	this->sunset = -1;

	this->atmosphere_humidity = 0;
	this->atmosphere_visibility = 0;
	this->atmosphere_pressure = 0;
	this->atmosphere_rising = 0;
 }

void WeatherInfo::setTimestamp( int64 ts ) {
	if ( ts == -1 )
		this->timestamp = Utilities::std_time();
	else
		this->timestamp = ts;
 }

void WeatherInfo::setPeriodDay( int64 start_ts ) {

	this->period_start = start_ts;
	this->period_end = start_ts + 60 * 60 * 24;

	this->period_name = "";
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WeatherInfo& WeatherInfo::operator=(const WeatherInfo& m) {
	if (this == &m) return *this; // same object?

	this->source_id = m.source_id;
	this->timestamp = m.timestamp;
	this->ttl = m.ttl;
	this->reliability = m.reliability;

	this->period_name = m.period_name;
	this->period_start = m.period_start;
	this->period_end = m.period_end;

	this->condition = m.condition;
	this->condition_x = m.condition_x;
	this->condition_key = m.condition_key;
	this->condition_summary = m.condition_summary;

	this->temperature = m.temperature;
	this->temperature_highest = m.temperature_highest;
	this->temperature_lowest = m.temperature_lowest;

	this->wind_chill = m.wind_chill;
	this->wind_direction = m.wind_direction;
	this->wind_speed = m.wind_speed;

	this->sunrise = m.sunrise;
	this->sunset = m.sunset;

	this->atmosphere_humidity = m.atmosphere_humidity;
	this->atmosphere_visibility = m.atmosphere_visibility;
	this->atmosphere_pressure = m.atmosphere_pressure;
	this->atmosphere_rising = m.atmosphere_rising;

	return *this;
 }

bool WeatherInfo::operator==(const WeatherInfo &other) const {
	if ( this->timestamp != other.timestamp )
		return false;
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool WeatherInfo::isEmpty() const {
	if ( this->timestamp == 0 ) return true;
	if ( this->period_start == 0 || this->period_end == 0 ) return true;
	if ( this->condition == WeatherInfo::CONDITION_UNKNOWN ) return true;
	return false;
 }

bool WeatherInfo::isPeriodValid() const {
	if ( this->period_start == 0 || this->period_end == 0 ) return false;
	return true;
 }

double WeatherInfo::evalSimilarity( const WeatherInfo& other ) const {
	double ret = 0.0;
	// ignore timestamp, period_name

	if ( this->condition == other.condition )
		ret += 0.5;

	if ( _GET_Abs(this->temperature_highest - other.temperature_highest ) / (double)this->temperature_highest < 10.0 )
		ret += 0.1;
	if ( _GET_Abs(this->temperature_lowest - other.temperature_lowest ) / (double)this->temperature_lowest < 10.0 )
		ret += 0.1;

	return ret;
 }

template <typename TYPE>
double mergeValues( TYPE& value0, const TYPE& value1, const TYPE& value_undefined, short merge_mode = 1 ) {
	if ( value0 == value1 ) return 1.0;
	if ( value0 == value_undefined ) {
		value0 = value1;
		return -1.0;
	 }
	else if ( value1 != value_undefined ) {
		TYPE sum = value0 + value1;
		double mean = (double)sum / 2;
		double var = ( (value0 - mean)*(value0 - mean) + (value1 - mean)*(value1 - mean) ) / 2;
		switch(merge_mode) {
			case 0: break;
			case 1:
			default: {
				value0 = (TYPE)mean;
				break;
			 }
		 }
		return var;
	 }
	return 0.0;
 }

double WeatherInfo::merge( const WeatherInfo& other ) {
	if ( isEmpty() ) {
		*this = other;
		return 0.0;
	 }
	if ( *this == other ) {
		return 1.0;
	 }
	double ret = 0.5;
	// real (smart) merge

	mergeValues(this->source_id, other.source_id, (int64)0, 0);
	mergeValues(this->timestamp, other.timestamp, (int64)0);
	mergeValues(this->ttl, other.ttl, (int64)0);
	mergeValues(this->reliability, other.reliability, 0.0);

	if ( this->period_name.empty() || !other.period_name.empty() )
		this->period_name = other.period_name;
	mergeValues(this->period_start, other.period_start, (int64)0, 1);
	mergeValues(this->period_end, other.period_end, (int64)0, 1);

	this->condition = this->condition | other.condition;
	this->condition_x = this->condition_x | other.condition_x;
	if ( this->condition_key.empty() || !other.condition_key.empty() )
		this->condition_key = other.condition_key;
	if ( this->condition_summary.empty() || !other.condition_summary.empty() )
		this->condition_summary = other.condition_summary;

	mergeValues(this->temperature, other.temperature, _WeatherInfo_TemperatureUndefined);
	mergeValues(this->temperature_highest, other.temperature_highest, _WeatherInfo_TemperatureUndefined);
	mergeValues(this->temperature_lowest, other.temperature_lowest, _WeatherInfo_TemperatureUndefined);

	mergeValues(this->wind_chill, other.wind_chill, 0);
	mergeValues(this->wind_direction, other.wind_direction, 0);
	mergeValues(this->wind_speed, other.wind_speed, 0.0);

	mergeValues(this->sunrise, other.sunrise, -1L);
	mergeValues(this->sunset, other.sunset, -1L);

	mergeValues(this->atmosphere_humidity, other.atmosphere_humidity, 0);
	mergeValues(this->atmosphere_visibility, other.atmosphere_visibility, 0.0);
	mergeValues(this->atmosphere_pressure, other.atmosphere_pressure, 0.0);
	mergeValues(this->atmosphere_rising, other.atmosphere_rising, 0);

	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool WeatherInfo::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->timestamp ) & ret;
	ret = hash_context->update( this->period_name ) & ret;
	ret = hash_context->update( this->period_start ) & ret;
	ret = hash_context->update( this->period_end ) & ret;
	ret = hash_context->update( this->condition_key ) & ret;
	ret = hash_context->update( this->condition_summary ) & ret;
	ret = hash_context->update( this->temperature_highest ) & ret;
	ret = hash_context->update( this->temperature_lowest ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<WeatherInfo>* WeatherInfo::getDataFields() {
	DataFieldCollector<WeatherInfo>* fields = new DataFieldCollector<WeatherInfo>();
	fields->add<int64, Variant>(&WeatherInfo::getPeriodStart, "PeriodStart", 0);
	fields->add<int64, Variant>(&WeatherInfo::getPeriodEnd, "PeriodEnd", 1);
	fields->add<const std::string&, Variant>(&WeatherInfo::getPeriodName, "PeriodName", 2);
	fields->add<int, Variant>(&WeatherInfo::getCondition, "Condition", 3);
	fields->add<int, Variant>(&WeatherInfo::getConditionExtra, "ConditionExtra", 4);
	fields->add<const std::string&, Variant>(&WeatherInfo::getConditionKey, "ConditionKey", 5);
	fields->add<const std::string&, Variant>(&WeatherInfo::getConditionSummary, "ConditionSummary", 6);
	fields->add<int, Variant>(&WeatherInfo::getTemperatureHighest, "TemperatureHighest", 7);
	fields->add<int, Variant>(&WeatherInfo::getTemperatureLowest, "TemperatureLowest", 8);
	return fields;
 }

IDataFieldWrapper<WeatherInfo>* WeatherInfo::getPrimaryKeyWrapper() {
	DataFieldWrapperT<WeatherInfo, int64, Variant>* dfw;
	dfw = new DataFieldWrapperT<WeatherInfo, int64, Variant>(&WeatherInfo::getPeriodStart, "PeriodStart", 0);
	return dfw;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::xtended::WeatherCondition_Updated, "wosh::Fact", 1.0, _static_WeatherCondition_Updated )

WeatherCondition_Updated::WeatherCondition_Updated( wosh::xtended::WeatherInfo* info )
	: wosh::Fact( _WeatherInfo_EVENT_WeatherConditionUpdated ) {
	setData( info );
 }

WeatherCondition_Updated::WeatherCondition_Updated( wosh::xtended::WeatherInfo* info, double similarity )
	: wosh::Fact( _WeatherInfo_EVENT_WeatherConditionUpdated ) {
	List* args = List::createFill( info, new Variant(similarity) );
	setData(args);
 }

const wosh::xtended::WeatherInfo* WeatherCondition_Updated::getWeatherInfo() const {
	if ( !hasData() ) return NULL;
	if ( getData()->isKindOf<wosh::xtended::WeatherInfo>() ) {
		return getData()->as<wosh::xtended::WeatherInfo>();
	 }
	if ( getData()->isKindOf<List>() ) {
		const List* list = getData()->as<List>();
		if ( list == NULL ) return NULL;
		if ( list->size() < 2 ) return NULL;
		if ( list->itemAt(0) == NULL ) return NULL;
		return list->itemAt(0)->as<wosh::xtended::WeatherInfo>();
	 }
	return NULL;
 }

double WeatherCondition_Updated::getSimilarity() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return -1.0;
	if ( var->isNumeric() ) return -1.0;
	return var->toDouble(-1.0);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace xtended
}; // namespace wosh
